﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Animations;
using Silvermoon.Controls.Base;

namespace Silvermoon.Controls.Beta
{
    /// <summary>
    /// Collection of <see cref="ItemBase"/> that support lazy loading and virtual size.
    /// Count is not readonly and thus it is possible to specify the desired number of items to maintain without actually adding them to the collection.
    /// If an item is accessed that is not yet added to the collection, the collection raises  GetVirtualItem() on the specified IItemControl to get the item and adds it to the collection.
    /// </summary>
    public class GroupItemCollection : IEnumerable<ItemBase>, IItemCollection
    {
        #region classes

        class Enumerator : IEnumerator<ItemBase>
        {
            private ItemBase[] items;
            private int count;
            private int index = -1;

            public Enumerator(ItemBase[] items, int count)
                : base()
            {
                this.items = items;
                this.count = count;
            }

            #region IEnumerator<ItemBase> Members

            public ItemBase Current
            {
                get { return items[index]; }
            }

            #endregion

            #region IDisposable Members

            public void Dispose()
            {

            }

            #endregion

            #region IEnumerator Members

            object System.Collections.IEnumerator.Current
            {
                get { return items[index]; }
            }

            public bool MoveNext()
            {
                return ++index < count;
            }

            public void Reset()
            {
                index = -1;
            }

            #endregion
        }


        #endregion
        #region fields
        protected IItemControl control;
        protected Control owner;
        private ItemBase[] items;
        private int arraySize;
        private int count;

        #endregion
        #region ctor/dtor

        public GroupItemCollection(IItemControl control)
            : base()
        {
            this.control = control;
            this.owner = (Control)control;
            EnsureCapacity(8);
        }

        public GroupItemCollection(IItemControl control, int size)
            : base()
        {
            if (size < 1) throw new IndexOutOfRangeException("size");
            this.control = control;
            EnsureCapacity(size);
        }

        #endregion
        #region props

        /// <summary>
        /// Gets whether the collection is virtual. This is the case when <see cref="P:Count"/> is set to a value other than 0.
        /// </summary>
        public bool IsVirtual { get; private set; }

        /// <summary>
        /// Gets or sets the number if items.
        /// </summary>
        public int Count
        {
            get { return count; }
            set
            {
                if (value < 0) value = 0;
                if (count != value)
                {
                    if (value > 0) IsVirtual = true;
                    EnsureCapacity(value + 1);
                    count = value;
                    Invalidated();
                }
            }
        }

        public ItemBase GetItemOrNull(int index)
        {
            CheckIndexRange(index);
            ItemBase value = items[index];
            return value;
        }

        internal ItemBase CreateVirtualItem(int index)
        {
            ItemBase value = control.GetVirtualItem(index);
            items[index] = value;
            value.Collection = this;
            return value;
        }


        public ItemBase this[int index]
        {
            get
            {
                CheckIndexRange(index);
                ItemBase value = items[index];
                if (value == null)
                {
                    value = control.GetVirtualItem(index);
                    value.Collection = this;
                    items[index] = value;
                }
                return value;
            }
            set
            {
#if DEBUG
                if (index < 0) throw new IndexOutOfRangeException();
#endif
                if (arraySize <= index) EnsureCapacity(index + 1);
                if (count < (index - 1))
                {
                    count = index - 1;
                }
                value.Collection = this;
                items[index] = value;
                Invalidated();
                OnCollectionChanged();
            }
        }

        private void CheckIndexRange(int index)
        {
            if (index < 0 || index >= count) throw new IndexOutOfRangeException();
        }

        #endregion
        #region IEnumerable<ItemBase> Members

        public IEnumerator<ItemBase> GetEnumerator()
        {
            return new Enumerator(items, count);
        }

        #endregion
        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return new Enumerator(items, count);
        }

        #endregion
        #region methods

        public ListBoxBase GetListBox()
        {
            return control.ListBox;
        }

        ItemGroupBase IItemCollection.GetItemGroup()
        {
            return control as ItemGroupBase;
        }


        public ItemBase[] ToArray()
        {
            ItemBase[] array = new ItemBase[count];
            Array.Copy(items, array, count);
            return array;
        }

        /// <summary>
        /// Clears the list.
        /// </summary>
        public void Clear()
        {
            control.OnClear();
            count = 0;
            OnCollectionChanged();
        }

        /// <summary>
        /// Adds an item
        /// </summary>
        public void Add(ItemBase item)
        {
            this[count++] = item;
        }

        public void AddItems(params ItemBase[] items)
        {
            AddRange(items);
        }

        public void AddRange(IEnumerable<ItemBase> items)
        {
            ICollection<ItemBase> col = items as ICollection<ItemBase>;
            if (col != null)
            {
                int count = col.Count;
                EnsureCapacity(this.count + col.Count + 1);
                col.CopyTo(this.items, this.count);
                this.count += count;
            }
            else
            {
                foreach (ItemBase item in items) Add(item);
            }
        }

        /// <summary>
        /// Inserts an item.
        /// </summary>
        public void Insert(int index, ItemBase item)
        {
            item.Collection = this;
            if (arraySize <= count + 1) EnsureCapacity(0);
            if (index < count)
            {
                Array.Copy(items, index, items, index + 1, count - index);
            }
            items[index] = item;
            this.count++;
            control.OnItemInserted(index, item);
            Invalidated();
            OnCollectionChanged();
        }

        /// <summary>
        /// Gets the index of an item.
        /// </summary>
        public int IndexOf(ItemBase item)
        {
            return Array.IndexOf<ItemBase>(items, item);
        }

        /// <summary>
        /// Removes an item.
        /// </summary>
        /// <param name="item"></param>
        public void Remove(ItemBase item)
        {
            int index = IndexOf(item);
            RemoveItem(item, index);
        }

        /// <summary>
        /// Removes an item at an index.
        /// </summary>
        public void RemoveAt(int index)
        {
            ItemBase item = items[index];
            RemoveItem(item, index);
        }

        private void RemoveItem(ItemBase item, int index)
        {
            item.Collection = null;
            count--;
            if (index < count)
            {
                Array.Copy(items, index + 1, items, index, count - index);
            }
            items[count] = null;
            control.OnItemRemoved(index, item);
            Invalidated();
            OnCollectionChanged();
        }

        private void EnsureCapacity(int newSize)
        {
            if (newSize > 0 && newSize < arraySize) return;
            if (newSize == 0) newSize = arraySize + 1;
            {
                //                newSize = arraySize + 1;
                if (newSize < 16) newSize = 16;
                else if (newSize < 32) newSize = 32;
                else if (newSize < 64) newSize = 64;
                else if (newSize < 256) newSize = 256;
                else if (newSize < 512) newSize = 512;
                else if (newSize < 1024) newSize = 1024;
                else if (newSize < 2048) newSize = 2048;
                else newSize = newSize * 4 / 3;
            }

            ItemBase[] copy = new ItemBase[newSize];
            if (items != null)
            {
                Array.Copy(items, copy, items.Length);
            }
            items = copy;
            arraySize = newSize;
        }

        private void Invalidated()
        {
            control.ItemsInvalidated();
        }

        /// <summary>
        /// Zeros an item (make's it no longer available, so it must be requested again).
        /// </summary>
        internal void Zero(int index)
        {
            ItemBase item = items[index];
            if (item != null)
            {
                item.RemoveAnimationStates();
                // item.RemoveFromParent();  // Note: it's already removed with ChildItems.Clear() before.
            }
            items[index] = null;
        }

        private void OnCollectionChanged()
        {
            if (ListChanged != null) ListChanged(this.control, EventArgs.Empty);
        }


        #endregion
        #region Events

        /// <summary>
        /// Occurs when changes where made against the collection, e.g. Insert, Remove, Clear.
        /// </summary>
        public event EventHandler ListChanged;

        #endregion

    }
}
